#include "ses_adc.h"
#include "ses_common.h"



#define ADC_TEMP_TABLE_SIZE  (sizeof(adcTempTable) / sizeof(uint16_t))
#define ADC_TEMP_MIN           0    /* minimum temp [dC]      */
#define ADC_TEMP_MAX		  50	/* maximum temp [dC]	  */
#define ADC_TEMP_STEP         50    /* stepping in table [dC] */
#define ADC_TEMP_RANGE		  10	/* index of last element in adcTempTable */



static const uint16_t adcTempTable[] =
	{ 888, 762, 655, 560, 479, 410, 351, 301, 259, 223, 192 };

static const uint16_t celsiusTable[] =
	{   0,   5,  10,  15,  20,  25,  30,  35,  40,  45,  50 };



void adc_init(void) {
	//  set to input
	DDR(ADC_MIC_NEG_PORT) &= ~(1 << ADC_MIC_NEG_PIN);
	DDR(ADC_MIC_POS_PORT) &= ~(1 << ADC_MIC_POS_PIN);
	DDR(ADC_TEMP_PORT) &= ~(1 << ADC_TEMP_PIN);
	DDR(ADC_LIGHT_PORT) &= ~(1 << ADC_LIGHT_PIN);
	DDR(ADC_JOYSTICK_PORT) &= ~(1 << ADC_JOYSTICK_PIN);

	// pull up bit
	ADC_MIC_NEG_PORT &= ~(1 << ADC_MIC_NEG_PIN);
	ADC_MIC_POS_PORT &= ~(1 << ADC_MIC_POS_PIN);
	ADC_TEMP_PORT &= ~(1 << ADC_TEMP_PIN);
	ADC_LIGHT_PORT &= ~(1 << ADC_LIGHT_PIN);
	ADC_JOYSTICK_PORT &= ~(1 << ADC_JOYSTICK_PIN);

	// deactivate the pr bit
	PRR0 &= ~(1 << PRADC);

	// set reference voltage
	ADMUX |= ADC_VREF_SRC;

	// configure the ADCSRA register
	ADCSRA |= ADC_PRESCALE;
	ADCSRA |= (1 << ADEN);

	// disable auto trigger
	ADCSRA &= ~(1 << ADATE);

	// disable conversion
	ADCSRA &= ~(1 << ADSC);
}

uint16_t adc_read(uint8_t adc_channel) {

	if (adc_channel >= ADC_NUM) {
		return 0;
	}

	// set first 3 bits to 0
	ADMUX = (ADMUX & (ADMUX_MASK));

	// apply adc_channel
	ADMUX |= adc_channel;

	// start conversion
	ADCSRA |= (1 << ADSC);

	// wait until reading finished
	while ((ADCSRA & (1 << ADSC))) {
		asm("nop");
	}

	return (ADC);
}

void adc_disable(void) {
	// deactivate adc
	ADCSRA &= ~(1 << ADEN);

	// activate the pr bit
	PRR0 |= (1 << PRADC);
}

int16_t adc_convertTemp(uint16_t val) {

	uint16_t tempValue = 0;
	uint16_t minValue = adcTempTable[0];
	uint16_t maxValue = adcTempTable[ADC_TEMP_RANGE];

	// interpolation search
	if (val > minValue) { // smaller than min temperature (> 888)
		return ADC_TEMP_MIN;
	}
	if (val < maxValue) { // bigger than max temperature	(< 192)
		return ADC_TEMP_MAX;
	}
	uint8_t low = 0;
	uint8_t high = ADC_TEMP_RANGE;

	// interpolate the index
	double numerator = (double) (0 - ((val - minValue) * (high - low)));
	double denominator = (double) (0 - (maxValue - minValue));
	uint8_t mid = ((uint8_t) (numerator / denominator) - 1); // -1 because of index

	// interpolate temperature celsius value
	if (adcTempTable[mid] > val) {
		tempValue = interpolate(mid, mid + 1, val);
	} else if (adcTempTable[mid] < val) {
		tempValue = interpolate(mid - 1, mid, val);
	} else { // mid == val
		return (celsiusTable[mid] * ADC_TEMP_RANGE);
	}
	return tempValue;
}

uint16_t interpolate(uint8_t index1, uint8_t index2, uint16_t value) {
	uint16_t val_i1 = adcTempTable[index1];
	uint16_t val_i2 = adcTempTable[index2];
	uint16_t temp_1 = celsiusTable[index1] * ADC_TEMP_RANGE;
	uint16_t temp_2 = celsiusTable[index2] * ADC_TEMP_RANGE;

	// if value out of range return 0
	if (!(val_i1 > value && value > val_i2)) {
		return 0;
	}

	// else interpolate
	uint16_t interpolated = temp_1 +
			(val_i1 - value) *
			((double) (temp_2 - temp_1) / (double) (val_i1 - val_i2));

	return interpolated;
}

